package algebra.visitors.cursors;



import utils.exception.CoreReasoningException;
import utils.exception.OrderingDescriptionException;
import algebra.query.indexing.DescriptionIndex;
import algebra.query.indexing.IndexTreeNode;
import algebra.query.od.OrderDep;
import algebra.query.od.OrderDepOid;
import entities.assertion.ConceptAssertion;
import entities.concept.Concept;

public class CACursorDIOrderById extends CACursorDI {
	
	public CACursorDIOrderById(DescriptionIndex di, ConceptAssertion ca, IndexTreeNode root, Concept d) {
		super(di, ca, root, true, d);
	}

	private boolean inited = false;
	private CACursorDIOrderById lcur = null, rcur = null;
	//used to compare results obtained from current root, left subtree and right subtree.
	//only needed if the results needs a sort on ind names.
	private IndexTreeNode[] three; 


	
	private IndexTreeNode getCursorRoot() {
		return this.root;
	}
	
	//the result obtained from the current root
	public void pushMiddleInThree(IndexTreeNode node){
		this.three[1] = node;
	}

	public void initSubCursor(ConceptAssertion query, IndexTreeNode mylroot,
			IndexTreeNode myrroot) {
		if (mylroot != null) {
			CACursorDIOrderById lcur = new CACursorDIOrderById(this.di, query, mylroot, this.scanQueryDefCp);
			lcur.openCursor();
			this.lcur = lcur;
		}
		if (myrroot != null) {
			CACursorDIOrderById rcur = new CACursorDIOrderById(this.di, query, myrroot, this.scanQueryDefCp);
			rcur.openCursor();
			this.rcur = rcur;
		}

	}

	private boolean isOpenSubCursor(int lr) {
		switch (lr) {
		case 0:
			return this.lcur != null;
		case 1:
			return this.rcur != null;
		default:
			return false;
		}
	}

	private void closeLeftSubCursor() {
		this.lcur = null;
	}

	private void closeRightSubCursor() {
		this.rcur = null;
	}

	public boolean populateSubCursor(int lr) {
		IndexTreeNode candidate = null;
		switch (lr) {
		case 0:
			if (this.lcur == null) {
				return false;
			}
			candidate = this.lcur.getNextResultNode();
			this.three[0] = candidate;
			if (candidate == null) {
				this.closeLeftSubCursor();
				return false;
			} else {
				return true;
			}
		case 1:
			if (this.rcur == null) {
				return false;
			}
			candidate = this.rcur.getNextResultNode();
			this.three[2] = candidate;
			if (candidate == null) {
				this.closeRightSubCursor();
				return false;
			} else {
				return true;
			}
		}
		return false;
	}


	@Override
	public boolean openCursor() {
		this.three = new IndexTreeNode[3];
		return true;
	}

	@Override
	public ConceptAssertion getNextResult() {
		if (ca == null) {
			return null;
		}
		IndexTreeNode res = getNextResultNode();
		if (res != null) {
			return res.getNodeAssertion();
		}
		return null;
	}

	private IndexTreeNode getNextResultNode() {
		IndexTreeNode res = null;
		try {
			if (!inited) {
				// get first result
				IndexTreeNode found = this.di.getNextSortId(ca, this.root, star, null, this,
						this.resOd);
				inited = true;
//				if(found==null){
//					System.out.println("null found "+this.root);
//				}
			}
			res = this.getNextFromThree();
			inited = true;
		} catch (Exception e) {
			e.printStackTrace();
		}

		return res;
	}




	@Override
	public String toString() {
		return this.root.getID().toString();
	}
	
	private boolean orderAfter(ConceptAssertion l, ConceptAssertion r) throws OrderingDescriptionException, CoreReasoningException{
		OrderDep newod = OrderDep.replaceLastSortBy(this.di.getOd(), new OrderDepOid());
		return newod.compare(l, r) > 0;
	}

	private int getMinThreeValues(IndexTreeNode[] three)
			throws OrderingDescriptionException, CoreReasoningException {
		IndexTreeNode min = null;
		int loc = -1;
		for (int del = 0; del < 3; del++) {
			if (min == null
					|| (three[del] != null && orderAfter(
							min.getNodeAssertion(),
							three[del].getNodeAssertion()))) {
				min = three[del];
				if (min != null)
					loc = del;
			}
		}
//		if(loc < 0){
//			System.out.println("loc: "+loc);
//		}
		return loc;
	}

	public IndexTreeNode getNextFromThree()
			throws OrderingDescriptionException, CoreReasoningException {
		IndexTreeNode[] three = this.three;
		int loc = this.getMinThreeValues(three);
		IndexTreeNode min = null;
		switch (loc) {
		case -1:
			return null;
		case 0:
			// left subtree advances
			min = three[0];
			if (this.isOpenSubCursor(0)) {
				this.populateSubCursor(0);
			}
			return min;
		case 1:
			// center removed, no more!
			min = three[1];
			this.three[1] = null;
			return min;
		case 2:
			// right subtree advances
			min = three[2];
			if (this.isOpenSubCursor(1)) {
				this.populateSubCursor(1);
			}
			return min;
		}
		return null;
	}

	public boolean repetition() {
		return this.inited;
	}
	
}
